﻿/*
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL/
  
  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the
  License.
  
  The Original Code is [maashaack framework].
  
  The Initial Developers of the Original Code are
  Zwetan Kjukov <zwetan@gmail.com> and Marc Alcaraz <ekameleon@gmail.com>.
  Portions created by the Initial Developers are Copyright (C) 2004-2014
  the Initial Developers. All Rights Reserved.
  
  Contributor(s):
  
  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
  use your version of this file under the terms of the MPL, indicate your
  decision by deleting the provisions above and replace them with the notice
  and other provisions required by the LGPL or the GPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.
*/
package molecule.render.flash.components.bars
{
    import graphics.Direction;

    import molecule.components.Draggable;
    import molecule.components.Scrollbar;

    import system.hack;
    import system.signals.Signal;
    import system.signals.Signaler;

    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    
    use namespace hack ;
    
    /**
     * This class provides a skeletal implementation of all the <code class="prettyprint">Scrollbar</code> display components, 
     * to minimize the effort required to implement this interface.
     */
    public class CoreScrollbar extends CoreProgressbar implements Draggable, Scrollbar
    {
        /**
         * Creates a new CoreScrollbar instance.
         * @param init An object that contains properties with which to populate the newly instance. If init is not an object, it is ignored.
         */
        public function CoreScrollbar( init:Object = null )
        {
            super( init ) ;
        }
        
        /**
         * The bar display of the scrollbar.
         */
        public function get bar():*
        {
            return _bar ;
        }
        
        /**
         * Indicates if the layout direction of the thumb is invert.
         */
        public function get invert():Boolean
        {
            return _invert ;
        }
        
        /**
         * @private
         */
        public function set invert( b:Boolean ):void
        {
            _invert = b ;
            update() ;
        }
            
        /**
         * Indicates if the bar is dragging.
         */
        public function get isDragging():Boolean 
        {
            return _isDragging ;
        }
        
        /**
         * Determines how much the scrollbar's value will change when one of the arrow buttons is clicked. 
         * If the scrollbar is being used to control something like a text area, this should probably be set to one, to cause the text to scroll one line. 
         * If it is scrolling a picture or movie clip, it should probably be set to a larger amount. 
         */
        public function get lineScrollSize():Number
        {
            return _lineScrollSize ;
        }
        
        /**
         * @private
         */
        public function set lineScrollSize( value:Number ):void
        {
            _lineScrollSize = value > 1 ? value : 1 ;
        }
        
        /**
         * Determines the amount the value will change if the user clicks above or below the thumb. 
         * If this amount is 0 the thumb will move with the lineScrollSize value.
         */
        public function get pageSize():Number
        {
            return (_pageSize > 0) ? _pageSize : lineScrollSize ;
        }
        
        /**
         * @private
         */
        public function set pageSize( value:Number ):void
        {
            _pageSize = value > 0 ? value : 0 ;
        }
        
        /**
         * The thumb display of the scrollbar.
         */
        public function get thumb():*
        {
            return _thumb ;
        }
        
        /**
         * Determinates the size of the thumb.
         */
        public function get thumbSize():Number
        {
            if( isNaN( _thumbSize ) )
            {
                return _direction == Direction.HORIZONTAL ? ( h - _border.vertical ) : ( w - _border.horizontal ) ;
            }
            else
            {
                return _direction == Direction.HORIZONTAL ? Math.min( _thumbSize , w ) : Math.min( _thumbSize , h ) ;
            }
        }
        
        /**
         * @private
         */
        public function set thumbSize( value:Number ):void
        {
            _thumbSize = value ;
            update() ;
        }
        
        /**
         * Invoked when the bar is dragging.
         */
        public function dragging( ...args:Array ):void 
        {
            var ts:Number = thumbSize ;
            var oldPosition:Number = position ;
            var newPosition:Number ;
            if( direction == Direction.HORIZONTAL )
            {
                newPosition = _thumb.x / (bar.width - ts) * (_max - _min) + _min ;
                if ( _invert )
                {
                    newPosition = (_max + _min) - newPosition ;
                }
            }
            else
            {
                var range:Number = bar.height - ts ;
                newPosition = _min + ( range - _thumb.y ) / range * (_max - _min) ;
                if ( _invert == false )
                {
                    newPosition = (_max + _min) - newPosition ;
                }
            }
            setPosition( Math.round(newPosition) ) ;
            if(newPosition != oldPosition)
            {
                notifyDrag() ;
            }
        }
        
        /**
         * Dispatchs an event when the user drag the bar.
         */
        public function notifyDrag():void 
        {
            if( _drag.connected() )
            {
                _drag.emit( this ) ;
            }
        }
        
        /**
         * Dispatchs an event when the user start to drag the bar.
         */
        public function notifyStartDrag():void
        {
            if( _dragStarted.connected() )
            {
                _dragStarted.emit( this ) ;
            }
        }
        
        /**
         * Dispatchs an event when the user stop to drag the bar.
         */
        public function notifyStopDrag():void
        {
            if( _dragStopped.connected() )
            {
                _dragStopped.emit( this ) ;
            }
        }
        
        /**
         * Invoked when the user start to drag the bar.
         */
        public function startDragging( ...args:Array ):void 
        {
            notifyStartDrag() ;
            _isDragging = true ;
            if ( stage )
            {
                stage.addEventListener( MouseEvent.MOUSE_UP   , stopDragging ) ;
                stage.addEventListener( MouseEvent.MOUSE_MOVE , dragging     ) ;
            }
            if( _direction == Direction.HORIZONTAL )
            {
                _thumb.startDrag(false, new Rectangle(0 , 0 , bar.width - thumbSize , 0 ) ) ;
            }
            else
            {
                _thumb.startDrag(false, new Rectangle(0 , 0 , 0 , bar.height - thumbSize ) ) ;
            }
        }
        
        /**
         * Invoked when the user stop to drag the bar.
         */
        public function stopDragging( ...args:Array ):void 
        {
            _isDragging = false ;
            if ( stage )
            {
                stage.removeEventListener(MouseEvent.MOUSE_UP   , stopDragging) ;
                stage.removeEventListener(MouseEvent.MOUSE_MOVE , dragging ) ;
            }
            _thumb.stopDrag();
            notifyStopDrag() ;
        }
        
        /**
         * Invoked when the position value of the bar is changed.
         */
        protected override function viewPositionChanged( flag:Boolean=false ):void 
        {
            try
            {
                _fixPosition() ;
                if ( !isDragging )
                {
                    var range:Number ;
                    if( _direction == Direction.HORIZONTAL )
                    {
                        range = bar.width - thumbSize - _border.horizontal;
                        _thumb.x = border.left + _area.x + (_position - _min) / (_max - _min) * range ;
                        _thumb.y = border.top + _area.y ;
                        if ( _invert )
                        {
                            _thumb.x = border.left + _area.x + range - _thumb.x + border.right ;
                        }
                    }
                    else
                    {
                        range = bar.height - thumbSize - _border.vertical ;
                        _thumb.x = border.left + _area.x ;
                        _thumb.y = border.top + _area.y + (_position - _min) / (_max - _min) * range ;
                        if ( _invert )
                        {
                            _thumb.y = border.top + _area.y + range - _thumb.y + border.bottom ;
                        }
                    }
                }
            }
            catch( e:Error )
            {
                
            }
        }
        
        /**
         * @private
         */
        hack var _bar:Sprite ;
        
        /**
         * @private
         */
        hack var _drag:Signaler = new Signal() ;
        
        /**
         * @private
         */
        hack var _dragStopped:Signaler = new Signal() ;
        
        /**
         * @private
         */
        hack var _dragStarted:Signaler = new Signal() ;
        
        /**
         * @private
         */
        hack var _thumb:Sprite ;
        
        /**
         * @private
         */
        protected var _area:Rectangle = new Rectangle() ;
        
        /**
         * @private
         */
        protected var _invert:Boolean ;
        
        /**
         * @private
         */
        private var _isDragging:Boolean ;
        
        /**
         * @private
         */
        protected var _lineScrollSize:Number = 1 ;
        
        /**
         * @private
         */
        protected var _pageSize:Number = 0 ;
        
        /**
         * @private
         */
        protected var _thumbSize:Number ;
        
        /**
         * Fix the position of the bar to be within minimum and maximum.
         */
        protected function _fixPosition():void
        {
            if( _max > _min )
            {
                _position = Math.min(_position, _max) ;
                _position = Math.max(_position, _min) ;
            }
            else
            {
                _position = Math.max(_position, _max) ;
                _position = Math.min(_position, _min) ;
            }
        }
    }
}
